package com.wangzhixuan.commons.config;

import com.wangzhixuan.commons.Constant;
import com.wangzhixuan.commons.enums.CacheEnum;
import com.wangzhixuan.commons.shiro.*;
import com.wangzhixuan.commons.shiro.cache.ShiroSpringCacheManager;
import com.wangzhixuan.commons.shiro.captcha.DreamCaptcha;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.AnonymousFilter;
import org.apache.shiro.web.filter.mgt.DefaultFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Shiro的配置文件
 *
 * @author lixin
 * @since 2019-2-14
 */
@Configuration
public class ShiroConfig {
    /**
     * 验证码图片生成器
     */
    @Bean
    public DreamCaptcha dreamCaptcha(ShiroSpringCacheManager shiroSpringCacheManager) {
        DreamCaptcha dreamCaptcha = new DreamCaptcha(shiroSpringCacheManager);
        dreamCaptcha.setCacheName(CacheEnum.HALF_HOUR.getName());
        return dreamCaptcha;
    }

    /**
     * 安全管理器
     */
    @Bean
    public SecurityManager securityManager(List<Realm> realms,
                                           ShiroSpringCacheManager shiroSpringCacheManager,
                                           RememberMeManager rememberMeManager,
                                           SessionManager sessionManager,
                                           ModularRealmAuthenticator authenticator) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //设置自定义Realm
        securityManager.setAuthenticator(authenticator);
        securityManager.setRealms(realms);
        //将缓存管理器，交给安全管理器
        securityManager.setCacheManager(shiroSpringCacheManager);
        securityManager.setSessionManager(sessionManager);
        //记住密码管理
        securityManager.setRememberMeManager(rememberMeManager);
        return securityManager;
    }

    /**
     * 項目自定义的Realm
     */
    @Bean
    public ShiroDbRealm userRealm(ShiroSpringCacheManager shiroSpringCacheManager, CredentialsMatcher credentialsMatcher) {
        ShiroDbRealm realm = new ShiroDbRealm(shiroSpringCacheManager, credentialsMatcher);
        //启用身份验证缓存，即缓存AuthenticationInfo信息，默认false
        realm.setAuthenticationCachingEnabled(true);
        //缓存AuthenticationInfo信息的缓存名称
        realm.setAuthenticationCacheName(CacheEnum.AUTHENTICATION_CACHE.getName());
        //缓存AuthorizationInfo信息的缓存名称
        realm.setAuthorizationCacheName(CacheEnum.AUTHORIZATION_CACHE.getName());
        return realm;
    }

    /**
     * rememberMe管理器,cipherKey生成见{@code Base64}
     */
    @Bean
    public RememberMeManager rememberMeManager() {
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        rememberMeManager.setCipherKey(Base64.decode("5aaC5qKm5oqA5pyvAAAAAA=="));
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setHttpOnly(true);
        cookie.setMaxAge(7 * 24 * 60 * 60);
        rememberMeManager.setCookie(cookie);
        return rememberMeManager;
    }

    /**
     * shiro Filter
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager, Constant constant) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        shiroFilter.setLoginUrl("/unLogin");
        shiroFilter.setUnauthorizedUrl("/unAuth");
        Map<String, Filter> filters = new HashMap<>();
        filters.put(DefaultFilter.anon.toString(), new AnonymousFilter());
        filters.put(DefaultFilter.user.toString(), new ShiroAjaxSessionFilter());
        shiroFilter.setFilters(filters);

        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/swagger/**", DefaultFilter.anon.toString());
        filterMap.put("/v2/api-docs", DefaultFilter.anon.toString());
        filterMap.put("/webjars/**", DefaultFilter.anon.toString());
        filterMap.put("/swagger-resources/**", DefaultFilter.anon.toString());
        filterMap.put("/doc.html", DefaultFilter.anon.toString());

        filterMap.put("/static/**", DefaultFilter.anon.toString());
        filterMap.put(constant.getVisitPath() + "/**", DefaultFilter.anon.toString());
        filterMap.put("/favicon.ico", DefaultFilter.anon.toString());

        filterMap.put("/druid/**", DefaultFilter.anon.toString());

        filterMap.put("/unLogin", DefaultFilter.anon.toString());
        filterMap.put("/unAuth", DefaultFilter.anon.toString());
        filterMap.put("/**/login**", DefaultFilter.anon.toString());
        filterMap.put("/commons/**", DefaultFilter.anon.toString());
        filterMap.put("/webhooks", DefaultFilter.anon.toString());
        filterMap.put("/**/captcha.jpg", DefaultFilter.anon.toString());

        filterMap.put("/**", DefaultFilter.user.toString());
        shiroFilter.setFilterChainDefinitionMap(filterMap);

        return shiroFilter;
    }

    @Bean
    public SessionManager sessionManager(SessionDAO sessionDAO) {
        DefaultWebSessionManager sessionManager = new CustomDefaultWebSessionManager();
        Cookie sessionCookie = new SimpleCookie("X-Token");
        sessionCookie.setHttpOnly(true);
        sessionCookie.setMaxAge(7 * 24 * 60 * 60);
        sessionManager.setSessionIdCookie(sessionCookie);
        //设置全局会话超时时间 三十天
        sessionManager.setGlobalSessionTimeout(30 * 60 * 60 * 1000);
        //去掉url后面的 JSESSIONID
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        sessionManager.setSessionDAO(sessionDAO);
        return sessionManager;
    }

    @Bean
    public SessionDAO sessionDAO(ShiroSpringCacheManager shiroSpringCacheManager) {
        EnterpriseCacheSessionDAO sessionDao = new EnterpriseCacheSessionDAO();
        sessionDao.setCacheManager(shiroSpringCacheManager);
        sessionDao.setActiveSessionsCacheName(CacheEnum.ACTIVE_SESSION_CACHE.getName());
        return sessionDao;
    }

    /**
     * 在方法中 注入  securityManager ，进行代理控制
     */
    @Bean
    public MethodInvokingFactoryBean methodInvokingFactory(SecurityManager securityManager) {
        MethodInvokingFactoryBean methodInvokingFactory = new MethodInvokingFactoryBean();
        methodInvokingFactory.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        methodInvokingFactory.setArguments(securityManager);
        return methodInvokingFactory;
    }

    /**
     * shiro密码加密配置
     */
    @Bean
    public PasswordHash passwordHash() {
        PasswordHash passwordHash = new PasswordHash();
        passwordHash.setAlgorithmName("md5");
        passwordHash.setHashIterations(1);
        return passwordHash;
    }


    /**
     * 密码错误5次锁定半小时
     */
    @Bean
    public CredentialsMatcher retryLimitCredentialsMatcher(ShiroSpringCacheManager shiroSpringCacheManager, PasswordHash passwordHash) {
        RetryLimitCredentialsMatcher credentialsMatcher = new RetryLimitCredentialsMatcher(shiroSpringCacheManager);
        credentialsMatcher.setPasswordHash(passwordHash);
        return credentialsMatcher;
    }

    /**
     * 多个realm管理器
     */
    @Bean
    public ModularRealmAuthenticator authenticator(List<Realm> realms) {
        ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
        authenticator.setRealms(realms);
        authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
        return authenticator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
        proxyCreator.setProxyTargetClass(true);
        return proxyCreator;
    }
}
